package bancosys.tec.persist.persister.listener;

import java.lang.reflect.TypeVariable;

import jmine.tec.annotations.Unmodifiable;
import jmine.tec.persist.PersistMessages;
import jmine.tec.utils.reflection.GenericTypeResolverImpl;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

import bancosys.tec.persist.authorization.AuthorizableActionAware;
import bancosys.tec.persist.exception.PersistenceException;
import bancosys.tec.persist.persister.PersistenceAction;

/**
 * @author RodrigoK
 * @created: Apr 18, 2008
 * @param <T> A entidade que ao ser persistida, irá executar métodos abaixo de acordo com os listeners configurados. Só deverá ser utilizado
 * o listener quando houver necessidade de processos onde não há dependencia entre os componentes.
 */
@Unmodifiable
public abstract class AbstractActionAwarePersisterListener<T extends AuthorizableActionAware> implements AuthorizablePersisterListener<T> {

    private final Class<T> targetClass;

    /**
     * C'tor
     */
    @SuppressWarnings("unchecked")
    public AbstractActionAwarePersisterListener() {
        try {
            TypeVariable<Class<AbstractActionAwarePersisterListener>> typeVariable =
                    AbstractActionAwarePersisterListener.class.getTypeParameters()[0];
            this.targetClass =
                    (Class<T>) GenericTypeResolverImpl.getInstance().resolveTypeForFixedGenericType(typeVariable, this.getClass());
        } catch (IllegalArgumentException e) {
            throw new PersistenceException(PersistMessages.INVALID_BEAN_IMPLEMENTATION.create(this.getClass().getName()), e);
        }
    }

    /**
     * C'tor
     * 
     * @param targetClass the target type
     */
    public AbstractActionAwarePersisterListener(Class<T> targetClass) {
        super();
        this.targetClass = targetClass;
    }

    /**
     * Retorna a classe configurada via generics na implementacao do PersisterListener.
     * 
     * @return classe configurada via generics na implementacao do PersisterListener.
     */
    public final Class<T> getTargetClass() {
        return this.targetClass;
    }

    /**
     * Executado após o insert na base quente.
     * 
     * @param bean bean.
     * @param action action
     */
    public void afterInsert(T bean, Object action) {
        // pode ser implementado quem extender a classe e tiver interesse na action envolvida.
    }

    /**
     * Executado antes do insert na base quente.
     * 
     * @param bean bean.
     * @param action action
     */
    public void beforeInsert(T bean, Object action) {
        // pode ser implementado quem extender a classe e tiver interesse na action envolvida.
    }

    /**
     * Executado após o update na base quente.
     * 
     * @param bean bean.
     * @param action action
     */
    public void afterUpdate(T bean, Object action) {
        // pode ser implementado quem extender a classe e tiver interesse na action envolvida.
    }

    /**
     * Executado antes do update na base quente.
     * 
     * @param bean bean.
     * @param action action
     */
    public void beforeUpdate(T bean, Object action) {
        // pode ser implementado quem extender a classe e tiver interesse na action envolvida.
    }

    /**
     * Executado após o remove na base quente.
     * 
     * @param bean bean.
     */
    public void afterRemove(T bean) {
        // pode ser implementado quem extender a classe.
    }

    /**
     * Executado antes do remove na base quente.
     * 
     * @param bean bean.
     */
    public void beforeRemove(T bean) {
        // pode ser implementado quem extender a classe.
    }

    /**
     * Executa qualquer processo que precise ser executado durante a exclusão mas antes da validação.
     * 
     * @param bean bean.
     */
    public void doRemoveDependencies(T bean) {
        // nothing
    }

    /**
     * {@inheritDoc}
     */
    public void afterUpdateColdInstance(T target) {
    }

    /**
     * {@inheritDoc}
     */
    public void beforeAuthorization(PersistenceAction action, T target) {
    }

    /**
     * {@inheritDoc}
     */
    public void afterInsertIntoColdPartition(PersistenceAction action, T coldInstance) {
    }

    /**
     * {@inheritDoc}
     */
    public void beforeRejection(PersistenceAction action, T target) {
    }

    /**
     * Retorna o hashCode da classe
     * 
     * @return o hash code da classe.
     */
    @Override
    public final int hashCode() {
        return new HashCodeBuilder().append(this.getClass()).append(this.getTargetClass()).toHashCode();
    }

    /**
     * Verifica se a instancia atual e' igual o objeto passado como parametro. <br>
     * Os 2 objetos sao considerados iguais se eles sao da mesma classe.
     * 
     * @param obj o objeto que deve ser comparado.
     * @return <code>true</code> se a instancia atual for igual ao objeto passado como parametro, <code>false</code> caso contrario.
     */
    @Override
    public final boolean equals(Object obj) {
        if (obj == null || !this.getClass().equals(obj.getClass())) {
            return false;
        }
        AbstractActionAwarePersisterListener<?> l = (AbstractActionAwarePersisterListener<?>) obj;
        return new EqualsBuilder().append(this.getClass(), l.getClass()).append(this.getTargetClass(), l.getTargetClass()).isEquals();
    }
}
